package com.itheima;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.*;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;

    /*
      Spring Data Redis

      在Spring Boot 项目中，可以使用Spring Data Redis来简化Redis操作，maven坐标：

      <dependency>
      	<groupId>org.springframework.boot</groupId>
      	<artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
     */

@SpringBootTest
public class LearnSpringDataRedis {


    /**
     * Spring Data是一个用于简化数据库访问的开源框架。其主要目标是使得对数据的访问变得方便快捷，包含多个子项目：
     * <p>
     * Spring Data JDBC- 对JDBC的Spring Data存储库支持。
     * <p>
     * Spring Data JPA - 对JPA的Spring Data存储库支持。
     * <p>
     * Spring Data MongoDB - 对MongoDB的基于Spring对象文档的存储库支持。
     * <p>
     * Spring Data Redis - 从Spring应用程序轻松配置和访问Redis。
     * <p>
     * ... ...
     * <p>
     * Spring Data Redis 是属于 Spring Data 开源框架下的一个模块，作用就是简化对于 redis 的操作
     * <p>
     * Spring Data Redis中提供了一个高度封装的类：RedisTemplate，针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口，具体分类如下：
     * ValueOperations：string类型数据操作
     * SetOperations：set类型数据操作
     * ZSetOperations：zset类型数据操作
     * HashOperations：针对map类型的数据操作
     * ListOperations：针对list类型的数据操作
     */
    @Autowired
    private RedisTemplate redisTemplate; //配置文件中写了redis配置,RedisTemplate对象由SpringBoot框架管理


    /**
     * 操作String类型数据
     */
    @Test
    public void testString() {
        ValueOperations valueOperations = redisTemplate.opsForValue();

        /*
        默认的Key序列化器为：JdkSerializationRedisSerializer ,导致存进Redis的key值前面多了很多乱码,通过在@Configuration配置类中自定义RedisTemplate对象交给Spring容器管理代替Spring默认引入的对象,修改序列化器为StringRedisSerializer,让key不再乱码

        而value依然有乱码,因为使用的也是JdkSerializationRedisSerializer序列化器,但不必修改,因为SpringDataRedis框架存取时自动序列化反序列化,所以程序中取数据时不会乱码
         */

        valueOperations.set("city123", "beijing");

        String value = (String) valueOperations.get("city123");
        System.err.println(value);//beijing

        valueOperations.set("key1", "value1", 60L, TimeUnit.SECONDS);

        Boolean aBoolean = valueOperations.setIfAbsent("city123", "Nanjing");//等价于SETNX key value		只有在 key 不存在时才会设置 key 的值,存在则不赋值(Not if eXist)
        System.err.println(aBoolean);//false
        System.err.println(valueOperations.setIfAbsent("city1234", "Nanjing"));//true
    }


    /**
     * 操作Hash类型数据
     */
    @Test
    public void testHash() {
        HashOperations hashOperations = redisTemplate.opsForHash();
        //存值
        hashOperations.put("002", "name", "xiaoming");
        hashOperations.put("002", "age", "20");
        hashOperations.put("002", "address", "bj");
        //取值
        String age = (String) hashOperations.get("002", "age");
        System.err.println(age);//20

        //获得Hash结构中所有字段   HKEYS key 		获取哈希表中所有字段
        Set keys = hashOperations.keys("002");
        for (Object key : keys) {
            System.err.println(key);
            //name
            //age
            //address
        }

        //获得hash结构中所有的值   HVALS key 		获取哈希表中所有值
        List values = hashOperations.values("002");
        for (Object value : values) {
            System.err.println(value);
            //xiaoming
            //20
            //bj
        }

        //获得hash结构中所有键值对  HGETALL key 		获取在哈希表中指定 key 的所有字段和值
        Map entries = hashOperations.entries("002");
        entries.forEach(new BiConsumer<String,String>() {
            @Override
            public void accept(String hashKey, String value) {
                System.err.println(hashKey + ":" + value);
                //name:xiaoming
                //age:20
                //address:bj
            }
        });
    }


    /**
     * 操作List类型数据
     */
    @Test
    public void testList() {
        ListOperations listOperations = redisTemplate.opsForList();

        //存值
        listOperations.leftPush("mylist", "a");
        listOperations.leftPushAll("mylist", "b", "c", "d");

        //取值
        List<String> mylist = listOperations.range("mylist", 0L, -1L);
        for (String s : mylist) {
            System.err.println(s);
            //d
            //c
            //b
            //a
        }

        //获得长度
        Long size = listOperations.size("mylist");


        //出队列
        int intSize = size.intValue();
        for (int i = 0; i < intSize; i++) {
            String element = (String) listOperations.rightPop("mylist");
            System.err.println(element);
            //a
            //b
            //c
            //d
        }
    }

    /**
     * 操作set类型数据
     */
    @Test
    public void testSet() {
        SetOperations setOperations = redisTemplate.opsForSet();
        //存值
        setOperations.add("myset", "a", "b", "c", "a");
        //取值
        Set<String> myset = setOperations.members("myset");
        for (String s : myset) {
            System.err.println(s);
            //c
            //b
            //a
        }
        //删除成员
        setOperations.remove("myset", "b", "a");
        myset = setOperations.members("myset");
        for (String s : myset) {
            System.err.println(s);//c

        }
    }

    /**
     * 操作Zset(Sorted Set)类型数据
     */
    @Test
    public void testZset() {
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        //存值
        zSetOperations.add("myZset", "a", 10.0);
        zSetOperations.add("myZset", "b", 11.0);
        zSetOperations.add("myZset", "c", 12.0);
        zSetOperations.add("myZset", "a", 13.0);

        //取值
        Set<ZSetOperations.TypedTuple<String>> myZset = zSetOperations.rangeWithScores("myZset", 0, -1);
        for (ZSetOperations.TypedTuple<String> stringTypedTuple : myZset) {
            System.err.println(stringTypedTuple.getValue() + ":" + stringTypedTuple.getScore());
            //b:11.0
            //c:12.0
            //a:13.0
        }

        //修改分数
        zSetOperations.incrementScore("myZset", "b", 20);
        Set<String> myZset1 = zSetOperations.range("myZset", 0, -1);
        for (String s : myZset1) {
            System.err.println(s);
            //c
            //a
            //b
        }
        //删除成员
        zSetOperations.remove("myZset", "a", "b");
        myZset1 = zSetOperations.range("myZset", 0, -1);
        for (String s : myZset1) {
            System.err.println(s);
            //c
        }
    }


    /**
     * 通用操作
     */
    @Test
    public void testCommon() {
        //获取Redis中所有的key
        Set keys = redisTemplate.keys("*");
        for (Object key : keys) {
            System.err.println(key);
            //myhash
            //myset
            //myZset
            //name
            //002
        }
        //判断某个key是否存在
        Boolean itcast = redisTemplate.hasKey("itcast");
        System.err.println(itcast);//false
        //删除指定key
        redisTemplate.delete("myZset");
        //获取指定key对应的value的数据类型
        DataType myset = redisTemplate.type("myset");
        System.err.println(myset.name());//SET
    }


}
